<!DOCTYPE html>
<html lang="en">
	<head>
		<title>Sphere-Sketch</title>
		<meta charset="utf-8">
		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
		<style>
			body {
				font-family: Monospace;
				background-color: #CCC;
				color: #fff;
				margin: 0px;
				overflow: hidden;
			}
			#info {
				color: #000;
				position: absolute;
				top: 10px;
				width: 100%;
				text-align: center;
				z-index: 100;
				display:block;
			}
			#info a, .button { 
				color: #f00;
				font-weight: bold; text-decoration: underline; cursor: pointer 
			}
		</style>
	</head>

	<body>
		<div id="info">
			<a href="https://zhuanlan.zhihu.com/p/35594034" target="_blank">说明文档 - 知乎 </a> 
		</div>
		
		<script src="./lib/three.min.js"></script>
		<script src="./lib/OBJLoader.js"></script>
		<script src="./lib/ShaderMaterial.js" type = "module"></script>	
		
		<script id="hatch_vShaderCode" type="x-shader/x-vertex">
			varying vec2 vUv;
			void main(void) {
			  gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
			  vUv = uv;
			}
		</script>
		
		<script id="compose_vShaderCode" type="x-shader/x-vertex">
			varying vec2 vUv;
			void main() {
			  gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
			  vUv = uv;
			}
		</script>
		

		<script id="hatch_fShaderCode" type="x-shader/x-fragment">
			varying vec2 vUv;
			
			uniform sampler2D aTexture;
			uniform sampler2D hatch0;
			uniform sampler2D hatch1;
			uniform sampler2D hatch2;
			
			float shade(const in float shading, const in vec2 uv) {
			  float shadingFactor;
			  float stepSize = 1.0 / 3.0;
			  float alpha = 0.0;
			  float scaleWhite = 0.0;
			  float scaleHatch0 = 0.0;
			  float scaleHatch1 = 0.0;
			  float scaleHatch2 = 0.0;
			  if (shading <= stepSize) {
				alpha = 3.0 * shading;
				scaleHatch1 = alpha;
				scaleHatch2 = 1.0 - alpha;
			  }
			  else if (shading > stepSize && shading <= 2.0 * stepSize) {
				alpha = 3.0 * (shading - stepSize);
				scaleHatch0 = alpha;
				scaleHatch1 = 1.0 - alpha;
			  }
			  else if (shading > 2.0 * stepSize) {
				alpha = 3.0 * (shading - stepSize * 2.0);
				scaleWhite = alpha;
				scaleHatch0 = 1.0 - alpha;
			  }
			  shadingFactor = scaleWhite +
				scaleHatch0 * texture2D(hatch0, uv).r +
				scaleHatch1 * texture2D(hatch1, uv).r +
				scaleHatch2 * texture2D(hatch2, uv).r;
			  return shadingFactor;
			}
					
			void main() {
				//TODO 疑问？UV坐标是在平面上无限循环吗（贴图无限平铺像3dsmax一样）??  hatch贴图尺寸过小不行？
			  vec2 uv = vUv*1.0;//
			  vec2 uv2 = vUv.yx*1.0;
			  float shading = texture2D(aTexture, vUv).r + 0.1  ;
			  float crossedShading = shade(shading, uv) * shade(shading, uv2);
			  //float crossedShading = shade(shading, uv)* 0.5 + shade(shading, uv2)*0.5;
			  gl_FragColor = vec4(vec3(crossedShading), 1.0);
			}

		</script>	
		
		<script id="compose_fShaderCode" type="x-shader/x-fragment">
			uniform sampler2D depthtexture;
			uniform sampler2D normaltexture;
			uniform sampler2D hatchtexture;

			varying vec2 vUv;

			float planeDistance(const in vec3 positionA, const in vec3 normalA, 
								const in vec3 positionB, const in vec3 normalB) {
				vec3 positionDelta = positionB-positionA;
				float planeDistanceDelta = max(abs(dot(positionDelta, normalA)), abs(dot(positionDelta, normalB)));
				return planeDistanceDelta;
			}

			void main() {
				float depthCenter = texture2D(depthtexture, vUv).r;
				float px = 1.0/1000.0;//

				/*
				vec3 leftpos = vec3(vUv.s - px, vUv.t, 1.0 - texture2D(depthtexture, vec2(vUv.s - px, vUv.t)).r);
				vec3 rightpos = vec3(vUv.s + px, vUv.t, 1.0 - texture2D(depthtexture, vec2(vUv.s + px, vUv.t)).r);
				vec3 uppos = vec3(vUv.s, vUv.t - px, 1.0 - texture2D(depthtexture, vec2(vUv.s, vUv.t - px)).r);
				vec3 downpos = vec3(vUv.s, vUv.t + px, 1.0 - texture2D(depthtexture, vec2(vUv.s, vUv.t + px)).r);
				*/
				
				///这里加减px是在屏幕坐标上加减 因为normaltexture是webglrendertaget（屏幕）的纹理
				vec3 leftnor = texture2D(normaltexture, vec2(vUv.s - px, vUv.t)).xyz;
				vec3 rightnor = texture2D(normaltexture, vec2(vUv.s + px, vUv.t)).xyz;
				vec3 upnor = texture2D(normaltexture, vec2(vUv.s, vUv.t - px)).xyz;
				vec3 downnor = texture2D(normaltexture, vec2(vUv.s, vUv.t + px)).xyz;

				/*
				//非边界处 planeDist基本上约为(0,0)
				//planeEdgeFactor貌似只有在多模型深度图的情况下起作用。
				vec2 planeDist = vec2(
					planeDistance(leftpos, leftnor, rightpos, rightnor),
					planeDistance(uppos, upnor, downpos, downnor)
				);
				float planeEdge = 2.5 * length(planeDist);				
				float planeEdgeFactor = 1.0 - 0.1 * smoothstep(0.0, depthCenter, planeEdge);
				*/

				//在边界上,normEdge越大，
				float normEdge = max(length(leftnor - rightnor), length(upnor - downnor));
				//颜色因子，边界应该深色，为了边界线条不至于全黑乘以一个系数
				float normEdgeFactor;
				//用插值
				normEdgeFactor = 1.0 - 0.8 * smoothstep(0.0, 1.0, normEdge); 
				//用截断
				//if(normEdge>1.0)
				//	normEdgeFactor= 0.5;
				//else	
				//	normEdgeFactor= 1.0;
				//
				float edgeFactor= normEdgeFactor; //*planeEdgeFactor
				vec4 hatch = texture2D(hatchtexture, vUv);
				gl_FragColor = vec4(vec3(hatch * edgeFactor), 1.0);//黑色铅笔效果
				//gl_FragColor = vec4(hatch.x * edgeFactor, hatch.x * edgeFactor,1.0, 1.0);//蓝色圆珠笔效果
			}
		</script>
		
		<script>

			var mouseX = 0;
			var mouseY = 0;
			var windowHalfX = window.innerWidth / 2;
			var windowHalfY = window.innerHeight / 2;
			var view_distance = 150;//相机离物体的距离
			
			var objScene = new THREE.Scene();
			var comScene = new THREE.Scene();
			var objCamera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 0.1, 2000 );
			var	comCamera = new THREE.OrthographicCamera(-windowHalfX, windowHalfX, windowHalfY, -windowHalfY, 0.1, 2000);
			var renderer = new THREE.WebGLRenderer( { antialias: true } );
			var finalMesh = new THREE.Mesh();
			
			var pars = {
				minFilter: THREE.LinearFilter,
				magFilter: THREE.LinearFilter,
				format: THREE.RGBFormat,
				stencilBuffer: false
			}
			var	depthBuffer = new THREE.WebGLRenderTarget(window.innerWidth, window.innerHeight, pars);
			var	normalBuffer = new THREE.WebGLRenderTarget(window.innerWidth, window.innerHeight, pars);
			var hatchBuffer= new THREE.WebGLRenderTarget(window.innerWidth, window.innerHeight, pars);				
			var hatch0 = THREE.ImageUtils.loadTexture('./res/hatch-texture/h0.png');
			var hatch1 = THREE.ImageUtils.loadTexture('./res/hatch-texture/h1.png');
			var hatch2 = THREE.ImageUtils.loadTexture('./res/hatch-texture/h2.png');

			
			//自定义加载
			var myModel = './res/geo.obj';
			var myTexture = THREE.ImageUtils.loadTexture('./res/geo.png');
			
			
			init();
			//render();
			render_cycle();
			
			function init(){
				//场景
				//objScene.fog = new THREE.Fog(0x0000ee,0.1,2000);
				
				//相机				
				objCamera.position.set(0,0,view_distance);				
				objCamera.lookAt(objScene.position);
				objScene.add(objCamera);
				
				//灯光
				var ambientLight = new THREE.AmbientLight( 0xfff, 1.0 );//颜色 强度
				objScene.add(ambientLight);				
				var keyLight = new THREE.SpotLight(0xfff, 1, 5000, Math.PI / 6, 25);
                keyLight.position.set(100, 100, 100);
                keyLight.target.position.set(0, 0, 0);
                objScene.add(keyLight);								
				
				//渲染器				
				//renderer.setPixelRatio( window.devicePixelRatio );
				renderer.setSize( window.innerWidth, window.innerHeight );	
				renderer.setClearColor(0xeeeeee, 1.0);						
					
				//布局
				document.body.appendChild( renderer.domElement );				
				document.addEventListener( 'mousemove', onDocumentMouseMove, false );
				document.addEventListener( 'DOMMouseScroll' ,onDocumentMouseWheel, false);
				window.addEventListener( 'resize', onWindowResize, false );
												
				//模型
				var objloader = new THREE.OBJLoader();
				var obj;				
				objloader.load(
					myModel,
					function(obj){
						//console.log("obj is mesh?"+ obj instanceof THREE.Mesh);
						obj.position.set(0,-20,0);
						objScene.add(obj);
					},
					function(xhr){console.log("On obj model Progress.....");},
					function(xhr){console.log("OBJ model load Error.");}					
				);
				
				//材质
				
				
				//////////////
				//输出的合成				
				var finalPlaneGeometry = new THREE.PlaneBufferGeometry(window.innerWidth, window.innerHeight);					
				//将前几步输出到 FrameBuffer (也就是WebGLRenderTarget)的结果作为这个矩形表面的贴图
				//var finalMesh = new THREE.Mesh();
				finalMesh.geometry = finalPlaneGeometry;
				//finalMesh.material = new THREE.MeshBasicMaterial({color:0xff0000});
				
				//finalMesh.material = mk_composeMat();
				comScene.add(finalMesh);
				
				comCamera.lookAt(comScene.position);
				comCamera.position.set(0,0,100);	
				
			}
			function onWindowResize() {
				windowHalfX = window.innerWidth / 2;
				windowHalfY = window.innerHeight / 2;
				objCamera.aspect = window.innerWidth / window.innerHeight;
				objCamera.updateProjectionMatrix();
				renderer.setSize( window.innerWidth, window.innerHeight );
			}

			function onDocumentMouseMove( event ) {
				mouseX = ( event.clientX ) ;
				mouseY = ( event.clientY ) ;
			}
			
			function onDocumentMouseWheel( event ) {
				if(event.detail == -3){//向前滚
					view_distance -= 10;					
				}
				else{//向后滚
					view_distance += 10;
				}
				if(view_distance<0.1)view_distance=0.1;
			}
			
	
			function mk_hatchMat(){			
				var uniPars = {
					aTexture:{type:'t', value: myTexture},
					hatch0:{type:'t', value: hatch0},
					hatch1:{type:'t', value: hatch1},
					hatch2:{type:'t', value: hatch2}
				};

				var hatchMaterial =  new THREE.ShaderMaterial({
					uniforms : uniPars,
					vertexShader:document.getElementById('hatch_vShaderCode').textContent,
					fragmentShader:document.getElementById('hatch_fShaderCode').textContent
				}); 
				return hatchMaterial;
			}
			
			function mk_composeMat(){
				var uniPars = {
					depthtexture:{type:'t', value: depthBuffer.texture },
					normaltexture:{type:'t', value: normalBuffer.texture},
					hatchtexture:{type:'t', value: hatchBuffer.texture}
				};
					
				var composeMaterial =  new THREE.ShaderMaterial({
					uniforms : uniPars,
					vertexShader:document.getElementById('compose_vShaderCode').textContent,
					fragmentShader:document.getElementById('compose_fShaderCode').textContent
				}); 
				return composeMaterial;
			}
	
				
			////////渲染输出部分//////			
			function render_cycle() {
				requestAnimationFrame( render_cycle );
				render();
			}
			function render() {
				renderAnimate();
				renderDepth();
				renderNormal();
				renderHetch();
				
				return compose();
			}				
			function renderAnimate(){
				objCamera.position.x = view_distance * Math.cos( (360*mouseX/window.innerWidth-90)*3.14/180 );
				objCamera.position.z = view_distance * Math.sin( (360*mouseX/window.innerWidth-90)*3.14/180 );
				objCamera.position.y = view_distance * Math.sin( (180*mouseY/window.innerHeight-90)*3.14/180);
				return objCamera.lookAt( objScene.position );
			}
			function renderDepth(){		
				var depthMaterial = new THREE.MeshDepthMaterial();
				objScene.overrideMaterial = depthMaterial;
				renderer.setClearColor(0xFFFFFF);
				renderer.clearTarget(depthBuffer, true, true);
				return renderer.render(objScene, objCamera, depthBuffer);
			}			
			function renderNormal(){
				var normalMaterial = new THREE.MeshNormalMaterial();
				objScene.overrideMaterial = normalMaterial;
				renderer.setClearColor(0xFFFFFF);
				renderer.clearTarget(normalBuffer, true, true);
				return renderer.render(objScene, objCamera, normalBuffer);
			}
			function renderHetch(){
				var hatchMaterial = mk_hatchMat();
				objScene.overrideMaterial = hatchMaterial;
				renderer.setClearColor(0xFFFFFF);
				renderer.clearTarget(hatchBuffer, true, true);
				return renderer.render(objScene, objCamera, hatchBuffer);
			}
			
			function compose(){		
				finalMesh.material = mk_composeMat();
				return renderer.render(comScene, comCamera);
			}
			
	
	
		</script>
			
		

	</body>
</html>



